package com.fox.lock;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.RedisConnectionFailureException;

import java.util.UUID;

/**
 * 在分布式环境下 用于确保一个任务只在一台机器上执行
 * 利用了分布式锁实现
 */
public abstract class SingleTask extends Thread {
    Logger logger = LoggerFactory.getLogger(SingleTask.class);
    DistributedLockService distributedLockService = null;
    private static final String LOCK_PREFIX = "LOCK-";
    private String lockName = null;
    private String taskName = null;
    private String requestId = null;
    private long lockMills = 15000;
    private long sleepMills = 15000;
    //重置锁时间的线程 sleep的时间
    private long keepSleepMills=0;
    private volatile Thread mainThread = this;
    private volatile Thread resetThread=null;

    public SingleTask(DistributedLockService service, String taskName) {
       init(service,taskName);
    }

    public SingleTask(DistributedLockService service, String taskName, long lockMills, long sleepMills) {
        init(service,taskName,lockMills,sleepMills);
    }

    private final void init(DistributedLockService service, String taskName){
        this.distributedLockService = service;
        this.taskName = taskName;
        this.lockName = LOCK_PREFIX + taskName;
        this.requestId = UUID.randomUUID().toString();
        this.setName(taskName);
        this.keepSleepMills =sleepMills - 2000 < 0 ? sleepMills/2 : sleepMills - 2000;
    }
    private void init(DistributedLockService service, String taskName, long lockMills, long sleepMills){
        this.lockMills = lockMills;
        this.sleepMills = sleepMills;
        init(service,taskName);
    }

    public final boolean mainThreadAlive(){
        return mainThread.isAlive();
    }
    //检测重置线程是否挂了
    //重置线程会因为连接不上redis抛出异常挂了
    public final boolean resetThreadAlive(){
        return resetThread==null?false:resetThread.isAlive();
    }

    //重置锁的超时时间
    private final void resetExpire() {
        //主线程存活给锁续超时
        do {
            try {

                Thread.sleep(keepSleepMills);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!mainThreadAlive()) {
                break;
            }
            distributedLockService.addExpire(lockName, requestId,keepSleepMills+lockMills);

        } while ((mainThreadAlive()));

    }

    //续时线程
    private final void startExpireThread(){
        //只要主业务没有执行完 该线程就会在锁快过期的时候重置key的过期时间
        //用短时间超时+续时 而不是直接设较长的时间超时主要是因为 (单机挂了能快速释放锁 因为会自己超时）
        //执行完毕以后线程会自动结束
        Thread resetExpireThread = new Thread(new Runnable() {
            @Override
            public void run() {
                resetExpire();
            }
        });
        resetThread=resetExpireThread;
        resetExpireThread.start();
    }

    public abstract void excuteTask();

    @Override
    public final void run() {
        //获取锁
        try {
            distributedLockService.lock(lockName, requestId, lockMills, sleepMills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        startExpireThread();
        mainThread=this;
        //业务代码
        excuteTask();
        logger.debug("SingleTask-{} stopped", taskName);
        distributedLockService.tryRelease(lockName, requestId);
    }
}
